package com.css.zfzx.sjcj.modules.qhsiteclassresult.repository;

import com.css.zfzx.sjcj.modules.boundary.Point;
import com.css.zfzx.sjcj.modules.qhsiteclassresult.repository.entity.QhSiteClassResultEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.io.*;
import java.util.*;

@Slf4j
public class QhSiteClassUtil {

    public static String siteClassPath = "", rawName = "raw3";

    public static String[] levels = {"1-3", "4-6", "7-9", "10-13", "14-16", "17-18"};

    public static Map<String, Map<String, Map<String, String[]>>> levelToDataFolder2FileName22Entity2 = new HashMap<>();//raw2: {数据目录名称: {数据文件名称: [经纬度, 场地类别]}}

    public static Map<String, List<QhSiteClassResultEntity>> districtToEntities = new HashMap<>();

    public static void initialize (String sitePath) {
        siteClassPath = sitePath;
        log.info("正在初始化场地类别数据...");
        Point[] boundary = { new Point(0, 0), new Point(0, 90), new Point(180, 90), new Point(180, 0)};
        for (int j = 0; j < levels.length; j++) {
            String dirName = "", path = siteClassPath + rawName + File.separator + levels[j];
            File file = new File(path);
            String[] dirNames = file.list();
            if (dirNames != null) {
                Map<String, Map<String, String[]>> dataFolder2FileName22Entity2 = new HashMap<>();
                for (int i = 0; i < dirNames.length; i++) {
                    dirName = dirNames[i];
                    dataFolder2FileName22Entity2.put(dirName, null);
                }
                levelToDataFolder2FileName22Entity2.put(levels[j], dataFolder2FileName22Entity2);
            }
            wrapDataFolderToFileName2Entity2ByBoundary(boundary, levels[j]);
        }
        log.info("已初始化场地类别数据!");
    }


    public static List<QhSiteClassResultEntity> getRaw2EntitiesByBoundary (Point[] points, String level, Point[] boundary, boolean isCountry, Map<String, List<Integer>> already) {
        List<QhSiteClassResultEntity> result = new ArrayList<>();
        if (isCountry) wrapDataFolderToFileName2Entity2ByBoundary(boundary, level);
        else wrapDataFolderToFileName2Entity2ByBoundary(points, level);
        Map<String, Map<String, String[]>> dataFolder2FileName22Entity2 = levelToDataFolder2FileName22Entity2.get(level);
        if (dataFolder2FileName22Entity2 == null) return result;
        for (Map.Entry<String, Map<String, String[]>> entryI: dataFolder2FileName22Entity2.entrySet()) {

            String dirName = entryI.getKey();
            String[] dirParts = dirName.split("-");
            String usaClassi = dirParts[1], dir_boundary_coordinate = dirParts[2];

            //System.out.println("正在查询目录: " + dirName + "...");

            Point[] siteDirBoundary = parseFileName(dir_boundary_coordinate);
            boolean flag = false;
            if (isCountry) {
                flag = isPolygonsOverlap(siteDirBoundary, boundary);
            } else {
                flag = /*isPolygonsOverlap(siteDirBoundary, boundary) && */isPolygonsOverlap(siteDirBoundary, points);
            }
            if (flag) {
                boolean isIntersect = false;
                Map<String, String[]> fileName2Entity2 = entryI.getValue();
                if (fileName2Entity2 != null) {
                    for (Map.Entry<String, String[]> entryJ : fileName2Entity2.entrySet()) {
                        String fileName = entryJ.getKey();
                        String[] coordinateAndClass = entryJ.getValue();
                        String geo_coordinate = coordinateAndClass[0];

                        String[] fileNameParts = fileName.split("-");
                        String file_boundary_coordinate = fileNameParts[3];
                        Point[] siteFileBoundary = parseFileName(file_boundary_coordinate);
                        flag = isPolygonsOverlap(siteFileBoundary, boundary);
                        if (!flag) continue;

                        //System.out.println("______正在查询文件: " + fileName + "...");

                        List<Integer> geoIdx = null;
                        if (already != null) {
                            geoIdx = already.get(fileName);
                            if (geoIdx == null) {
                                geoIdx = new ArrayList<>();
                                already.put(fileName, geoIdx);
                            }
                        }

                        StringBuilder sb1 = new StringBuilder();
                        String[] geometries = StringUtils.split(geo_coordinate, "|");
                        int g_len = geometries.length, g_last_idx = g_len - 1;
                        for (int k = 0; k < g_len; k++) {

                            //System.out.println("_________正在校验第" + k + "个几何体...");

                            String[] coords1 = StringUtils.split(geometries[k], "#");
                            int c_len = coords1.length;
                            isIntersect = false;
                            for (int m = 0; m < c_len; m++) {
                                String[] coords2 = coords1[m].split(", ");
                                Point[] points2 = new Point[coords2.length];
                                for (int n = 0; n < coords2.length; n++) {
                                    String[] coords3 = StringUtils.split(coords2[n], " ");
                                    float lon = Float.parseFloat(coords3[0]), lat = Float.parseFloat(coords3[1]);
                                    Point point = new Point(lon, lat);
                                    points2[n] = point;
                                }
                                if (isCountry) {
                                    flag = isPolygonsOverlap(points2, boundary);
                                } else {
                                    flag = /*isPolygonsOverlap(points2, boundary) && */isPolygonsOverlap(points2, points);
                                }
                                if (flag) {
                                    isIntersect = true;
                                    break;
                                }
                            }
                            if (isIntersect) {
                                if (geoIdx != null) {
                                    if (geoIdx.indexOf(k) != -1) {
                                        //System.out.println(fileName + ": 已过滤第" + k + "个重复几何体.");
                                        continue;
                                    } else geoIdx.add(k);
                                }
                            }

                            if (k != g_last_idx) {
                                if (isIntersect) sb1.append(geometries[k] + "|");
                            } else {
                                if (isIntersect) sb1.append(geometries[k]);
                            }
                        }
                        String lonlatStr = sb1.toString();
                        if (lonlatStr.length() > 0) {
                            if (lonlatStr.endsWith("|")) lonlatStr = lonlatStr.substring(0, lonlatStr.length() - 1);
                            QhSiteClassResultEntity qhSiteClassResultEntity = new QhSiteClassResultEntity();
                            qhSiteClassResultEntity.setCoordinates(lonlatStr);
                            qhSiteClassResultEntity.setUsaClassi(usaClassi);
                            result.add(qhSiteClassResultEntity);
                        }
                    }
                }
            }
        }
        return result;
    }
    private static void wrapDataFolderToFileName2Entity2ByBoundary (Point[] points, String level) {
        String path = siteClassPath + rawName + File.separator + level;
        Map<String, Map<String, String[]>> dataFolder2FileName22Entity2 = levelToDataFolder2FileName22Entity2.get(level);
        if (dataFolder2FileName22Entity2 == null) return;
        for (Map.Entry<String, Map<String, String[]>> entryJ: dataFolder2FileName22Entity2.entrySet()) {
            String dirName = entryJ.getKey();
            String[] dirParts = dirName.split("-");
            String usaClassi = dirParts[1], dir_boundary_coordinate = dirParts[2];
            Point[] siteDirBoundary = parseFileName(dir_boundary_coordinate);
            if (isPolygonsOverlap(siteDirBoundary, points)) {
                Map<String, String[]> fileName2Entity2 = entryJ.getValue();
                if (fileName2Entity2 == null) {
                    fileName2Entity2 = new HashMap<>();
                    File raw2File = new File(path + File.separator + dirName);
                    File[] raw2Files = raw2File.listFiles();
                    for (int j = 0; j < raw2Files.length; j++) {
                        String fileName = raw2Files[j].getName();
                        fileName = fileName.substring(0, fileName.indexOf("."));
                        if (!fileName2Entity2.containsKey(fileName)) {
                            String absPath = raw2Files[j].getAbsolutePath();
                            //System.out.println(absPath);
                            String geo_coordinate = readSiteClassFile(absPath);
                            fileName2Entity2.put(fileName, new String[]{geo_coordinate, usaClassi});
                        }
                    }
                    dataFolder2FileName22Entity2.put(dirName, fileName2Entity2);
                }
            }
        }
    }
    private static Point[] parseFileName (String coordinate) {
        String[] lonLatStr = coordinate.replace("_", ".").split("=");
        float minLon = Float.parseFloat(lonLatStr[0]), minLat = Float.parseFloat(lonLatStr[1]), maxLon = Float.parseFloat(lonLatStr[2]), maxLat = Float.parseFloat(lonLatStr[3]);
        Point[] siteBoundary = wrapBoundary(minLon, minLat, maxLon, maxLat);
        return siteBoundary;
    }
    public static Point[] wrapBoundary (float minLon, float minLat, float maxLon, float maxLat) {
        Point[] siteBoundary = new Point[4];
        siteBoundary[0] = new Point(minLon, minLat);
        siteBoundary[1] = new Point(minLon, maxLat);
        siteBoundary[2] = new Point(maxLon, maxLat);
        siteBoundary[3] = new Point(maxLon, minLat);
        return siteBoundary;
    }

    private static String readSiteClassFile(String filepath) {
        StringBuffer sb = new StringBuffer();
        try {
            FileReader fileReader = new FileReader(filepath);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String line = bufferedReader.readLine();
            while (line != null){
                sb.append(line);
                line = bufferedReader.readLine();
            }
            bufferedReader.close();
            fileReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sb.toString();
    }

    /*public static void main(String[] args) {

        String siteClassPath = "E:\\IdeaProjects\\upload\\files\\qhwx\\site\\全国1-100万宏观场地类别\\";
        initialize(siteClassPath);

        System.out.println(System.getProperty("user.dir"));

    }*/


    /**
     * 检查多边形是否包含了某点~多边形都是由点组成
     *
     * @param px 纬度
     * @param py 经度
     * @param xD 多边形所有纬度
     * @param yD 多边形所有经度
     * @return
     */
    public static boolean containsPoint(double px, double py, ArrayList<Double> xD, ArrayList<Double> yD) {
        int verticesCount = xD.size();
        int nCross = 0;
        for (int i = 0; i < verticesCount; ++i) {
            double ix = xD.get(i);
            double iy = yD.get(i);
            double i1x = xD.get((i + 1) % verticesCount);
            double i1y = yD.get((i + 1) % verticesCount);

            // 求解 y=p.y 与 p1 p2 的交点
            if (iy == i1y) {   // p1p2 与 y=p0.y平行
                continue;
            }
            if (py < Math.min(iy, i1y)) { // 交点在p1p2延长线上
                continue;
            }
            if (py >= Math.max(iy, i1y)) { // 交点在p1p2延长线上
                continue;
            }
            // 求交点的 X 坐标
            float x = (float) ((py - iy) * (i1x - ix)
                    / (i1y - iy) + ix);
            if (x > px) { // 只统计单边交点
                nCross++;
            }
        }
        // 单边交点为偶数，点在多边形之外
        return (nCross % 2 == 1);
    }

    public static boolean isSegmentsIntersect(Point[] segA, Point[] segB) {
        Point segA0 = segA[0], segA1 = segA[1];
        Point segB0 = segB[0], segB1 = segB[1];
        double abc = (segA0.x - segB0.x) * (segA1.y - segB0.y) - (segA0.y - segB0.y) * (segA1.x - segB0.x);
        double abd = (segA0.x - segB1.x) * (segA1.y - segB1.y) - (segA0.y - segB1.y) * (segA1.x - segB1.x);
        if (abc * abd >= 0) {
            return false;
        }
        double cda = (segB0.x - segA0.x) * (segB1.y - segA0.y) - (segB0.y - segA0.y) * (segB1.x - segA0.x);
        double cdb = cda + abc - abd;
        return !(cda * cdb >= 0);
    }

    public static boolean isPolygonsIntersect(Point[] plyA, Point[] plyB) {
        for (int i = 0, il = plyA.length; i < il; i++) {
            for (int j = 0, jl = plyB.length; j < jl; j++) {
                Point[] segA = {plyA[i], plyA[i == il - 1 ? 0 : i + 1]};
                Point[] segB = {plyB[j], plyB[j == jl - 1 ? 0 : j + 1]};
                if (isSegmentsIntersect(segA, segB)) {
                    return true;
                }
            }
        }
        return false;
    }

    //判断点是否在另一平面图中
    public static boolean isPointInPolygon(Point point, Point[] polygon) {

        //下述代码来源：http://paulbourke.net/geometry/insidepoly/，进行了部分修改
        //基本思想是利用射线法，计算射线与多边形各边的交点，如果是偶数，则点在多边形外，否则
        //在多边形内。还会考虑一些特殊情况，如点在多边形顶点上，点在多边形边上等特殊情况。

        int N = polygon.length;
        boolean boundOrVertex = true; //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
        int intersectCount = 0; //cross points count of x
        double precision = 2e-10; //浮点类型计算时候与0比较时候的容差
        Point p1, p2; //neighbour bound vertices
        Point p = point; //测试点

        p1 = polygon[0]; //left vertex
        for (int i = 1; i <= N; ++i) { //check all rays
            if (p.x == p1.x && p.y == p1.y) {
                return boundOrVertex; //p is an vertex
            }

            p2 = polygon[i % N]; //right vertex
            if (p.y < Math.min(p1.y, p2.y) || p.y > Math.max(p1.y, p2.y)) { //ray is outside of our interests
                p1 = p2;
                continue; //next ray left point
            }

            if (p.y > Math.min(p1.y, p2.y) && p.y < Math.max(p1.y, p2.y)) { //ray is crossing over by the algorithm (common part of)
                if (p.x <= Math.max(p1.x, p2.x)) { //x is before of ray
                    if (p1.y == p2.y && p.x >= Math.min(p1.x, p2.x)) { //overlies on a horizontal ray
                        return boundOrVertex;
                    }

                    if (p1.x == p2.x) { //ray is vertical
                        if (p1.x == p.x) { //overlies on a vertical ray
                            return boundOrVertex;
                        } else { //before ray
                            ++intersectCount;
                        }
                    } else { //cross point on the left side
                        double xinters = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x; //cross point of x
                        if (Math.abs(p.x - xinters) < precision) { //overlies on a ray
                            return boundOrVertex;
                        }

                        if (p.x < xinters) { //before ray
                            ++intersectCount;
                        }
                    }
                }
            } else { //special case when ray is crossing through the vertex
                if (p.y == p2.y && p.x <= p2.x) { //p crossing over p2
                    Point p3 = polygon[(i + 1) % N]; //next vertex
                    if (p.y >= Math.min(p1.y, p3.y) && p.y <= Math.max(p1.y, p3.y)) { //p.y lies between p1.y & p3.y
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2; //next ray left point
        }

        if (intersectCount % 2 == 0) { //偶数在多边形外
            return false;
        } else { //奇数在多边形内
            return true;
        }
    }

    //判断两多变形是否存在点与区域的包含关系(A的点在B的区域内或B的点在A的区域内)
    public static boolean isPointInPolygonBidirectional(Point[] plyA, Point[] plyB) {//面面
        boolean a = false, b = false;
        for (int i = 0; i < plyA.length; i++) {
            if (isPointInPolygon(plyA[i], plyB)) {
                a = true;
                break;
            }
        }
        if (!a) {
            for (int i = 0; i < plyB.length; i++) {
                if (isPointInPolygon(plyB[i], plyA)) {
                    b = true;
                    break;
                }
            }
        }
        return a || b;
    }

    public static boolean isPolygonsOverlap(Point[] plyA, Point[] plyB) {
        return isPolygonsIntersect(plyA, plyB) || isPointInPolygonBidirectional(plyA, plyB);
    }


}
